/*******************************************************************************
 * Copyright (c) baifeiq.
 * 
 * @date            @author         @brief
 * 2021-07-12       baifeiq         First version: The management of rtos task for 
 *                                  ByOs.
 * 2021-12-30       baifeiq         Reorganize the coding style.
 * 2022-01-11       baifeiq         Integrate task readiness table and state into 
 *                                  os_sys_param.
 * 2022-01-11       baifeiq         Add task suspension and activation function 
 *                                  and its interface.
 * 2022-02-21       baifeiq         Add system initial monitoring parameter.
 */

#include "os_define.h"
#include "os_task.h"
#include "os_scheduler.h"
#include "os_list.h"

extern os_task_t *task_current;

/* System monitoring parameter. */
os_sys_para_t os_sys_param = 
{
    .os_version     = "2.0.2",
    .os_time        = 0,
    .ready_table    = 0u,
    .task_current   = OS_NULL,
    .task_last      = OS_NULL,
    .task_next      = OS_NULL,
    .task_table     = {OS_NULL},
    .task_delay     = OS_NULL
};

/**
 * @brief Interface function of initialization task.
 * 
 * @param task Data structure object for task.
 * @param entry Function to realize tasks.
 * @param param Enter parameters for task functions.
 * @param Pro Priority of task.
 * @param stack_addr Stack of task.
 */
os_int32_t ost_task_init(os_task_t   *task, 
                         void        (*entry)(void *), 
                         void        *param, 
                         os_uint8_t  pro, 
                         os_uint32_t *stack_addr)
{
	OS_NULL_CHECK(task);
	OS_NULL_CHECK(stack_addr);
	if (pro > OS_TASK_MAX)
	{
		return -1;
	}
	
	*(--stack_addr) = (unsigned long)(1<<24);		//xPSR
	*(--stack_addr) = (unsigned long)entry;			//PC,R13
	*(--stack_addr) = (unsigned long)0x14;			//LR
	*(--stack_addr) = (unsigned long)0x12;			//R12
	*(--stack_addr) = (unsigned long)0x03;			//R3
	*(--stack_addr) = (unsigned long)0x02;			//R2
	*(--stack_addr) = (unsigned long)0x01;			//R1
	*(--stack_addr) = (unsigned long)param;			//R0
	*(--stack_addr) = (unsigned long)0x11;			//R11
	*(--stack_addr) = (unsigned long)0x10;			//R10
	*(--stack_addr) = (unsigned long)0x09;			//R9
	*(--stack_addr) = (unsigned long)0x08;			//R8
	*(--stack_addr) = (unsigned long)0x07;			//R7
	*(--stack_addr) = (unsigned long)0x06;			//R6
	*(--stack_addr) = (unsigned long)0x05;			//R5
	*(--stack_addr) = (unsigned long)0x04;			//R4

	task->stack_addr        = stack_addr;
	task->pro               = pro;
	task->state             = OS_READY;
	task->timer             = 0;
    task->param             = OS_NULL;
    task->task_remove_call  = OS_NULL;
    task->event_msg         = OS_NULL;
    task->event_result      = 0;
    task->event             = OS_NULL;

	os_sys_param.task_table[pro] = task;
	
	os_bit_set(pro, &os_sys_param.ready_table);
 
	return 0;
}

/**
 * @brief System list init.
 */
void ost_sys_init(void)
{
    os_list_init(&os_sys_param.task_delay);
}

/**
 * @brief Indicates the tasks currently running on the system.
 * @param tl Last task of os system.
 * @param tn Next task of os system.
 * @param tc Current task of os system.
 */
void os_sys_param_update(os_task_t *tl, os_task_t *tn, os_task_t *tc)
{
    os_sys_param.task_last     = tl;
    os_sys_param.task_next     = tn;
    os_sys_param.task_current  = tc;
}

/**
 * @brief Call os SysTick_Handler.
 */
void os_systick(void)
{
    os_sys_param.os_time++;
}

/**
 * @brief Insert the task into the ready table.
 */
void os_insert_ready(os_task_t *task)
{
    os_bit_set(task->pro, &os_sys_param.ready_table);
}

/**
 * @brief Remove task from ready table.
 */
void os_remove_ready(os_task_t *task)
{
    os_bit_reset(task->pro, &os_sys_param.ready_table);
}

/**
 * @brief Task waiting delay function.
 * 
 * @param ms Task delay cycle.
 */
void ost_delay(unsigned int ms)
{
    os_uint32_t os_interrupt = os_critical_enter();

	task_current->timer = ms;
	task_current->state = OS_WAITE_DELAY;

    os_remove_ready(task_current);
    os_list_insert_after(&os_sys_param.task_delay ,&task_current->list_delay);

	os_sched();
    os_critical_quit(os_interrupt);
}

/**
 * @brief Delete a task node from the task delay list, and join this task to the
 *        ready table.
 * 
 * @param task Deleted task node.
 */
void ost_delay_wake(os_task_t *task)
{
    os_uint32_t os_interrupt = os_critical_enter();

    task->state = OS_READY;
    os_insert_ready(task);
    os_list_remove(&task->list_delay);

    os_critical_quit(os_interrupt);
}

/**
 * @brief Suspend the task and stop running.
 * 
 * @param task Suspended task.
 */
void ost_suspension(os_task_t *task)
{
    os_uint32_t os_interrupt = os_critical_enter();

    if (OS_READY != task->state)
    {
        task->state = OS_SUSPENSION;
        os_list_remove(&task->list_delay);

        if (task == task_current)
        {
            os_sched();
        }
    }

    os_critical_quit(os_interrupt);
}

/**
 * @brief Activate the task and re-join the delay queue.
 * 
 * @param task Activate task.
 */
void ost_activation(os_task_t *task)
{
    os_uint32_t os_interrupt = os_critical_enter();

    if (OS_SUSPENSION == task->state)
    {
        task->state   = OS_WAITE_DELAY;
        task->timer    = 0;
        os_list_insert_after(&os_sys_param.task_delay, &task->list_delay);

        os_sched();
    }

    os_critical_quit(os_interrupt);
}

/**
 * @brief Remove the task and clear the resource from the task.
 * 
 * @param task Remove task.
 */
void ost_task_remove(os_task_t *task)
{
    os_uint32_t os_interrupt = os_critical_enter();

    if (OS_SUSPENSION != task->state)
    {
        task->state = OS_SUSPENSION;
        os_list_remove(&task->list_delay);
    }

    if (task->task_remove_call)
    {
        task->task_remove_call(task->param);
    }

    if (task == task_current)
    {
        os_sched();
    }

    os_critical_quit(os_interrupt);
}

/**
 * @brief Set callback function for deleting task.
 * 
 * @param task Target task.
 * @param fun The callback function.
 * @param param Parameters of callback function.
 */
void ost_task_remove_callset(os_task_t *task, void (*fun)(void *param), void *param)
{
    task->task_remove_call = fun;
    task ->param = param;
}
